﻿using System;
using System.ComponentModel.Design;
using System.Globalization;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using EnvDTE;
using EnvDTE80;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.IO;
using Microsoft.Win32;

namespace NFinalCommand.Command
{
    /// <summary>
    /// Command handler
    /// </summary>
    internal sealed class MyCommand
    {
        /// <summary>
        /// Command ID.
        /// </summary>
        public const int CreateActionId = 0x0100;
        public const int CreateViewId = 0x0101;
        public const int DebugToHere = 0x0110;

        public EnvDTE80.DTE2 dte { get; set; }
        public IComponentModel componentModel { get; set; }
        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid CommandSet = new Guid("2fd8d796-808a-48e0-8e70-6f1548752123");

        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly Package package;

        /// <summary>
        /// Initializes a new instance of the <see cref="MyCommand"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        private MyCommand(Package package)
        {
            if (package == null)
            {
                throw new ArgumentNullException("package");
            }

            this.package = package;

            OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (commandService != null)
            {
                var menuCommandCreateActionID = new CommandID(CommandSet, CreateActionId);
                var menuItemCreateAction = new MenuCommand(this.MenuItemCreateActionCallback, menuCommandCreateActionID);

                var menuCommandCreateViewID = new CommandID(CommandSet, CreateViewId);
                var menuItemCreateView = new MenuCommand(this.MenuItemCreateViewCallback, menuCommandCreateViewID);

                var menuDebugToHereID = new CommandID(CommandSet, DebugToHere);
                var menuItemDebugToHere = new MenuCommand(this.MenuItemDebugToHereCallback, menuDebugToHereID);

                commandService.AddCommand(menuItemCreateAction);
                commandService.AddCommand(menuItemCreateView);
                commandService.AddCommand(menuItemDebugToHere);
            }
        }

        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static MyCommand Instance
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the service provider from the owner package.
        /// </summary>
        private IServiceProvider ServiceProvider
        {
            get
            {
                return this.package;
            }
        }

        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static void Initialize(Package package)
        {
            var myCommandPackage = (MyCommandPackage)package;
            Instance = new MyCommand(package);
            Instance.dte = myCommandPackage.dte;
            Instance.componentModel = myCommandPackage.componentModel;
        }

        /// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void MenuItemCreateActionCallback(object sender, EventArgs e)
        {
            EnvDTE.TextSelection ts = dte.ActiveDocument.Selection as EnvDTE.TextSelection;
            EnvDTE80.CodeClass2 cls = ts.ActivePoint.CodeElement[vsCMElement.vsCMElementClass] as EnvDTE80.CodeClass2;
            string projectName = cls.ProjectItem.ContainingProject.Name;
            EnvDTE80.CodeFunction2 func = ts.ActivePoint.CodeElement[vsCMElement.vsCMElementFunction]
                as EnvDTE80.CodeFunction2;
            //ShowMessage(cls.Namespace.Name+":"+cls.Name+":"+func.Name+":"+func.Type.AsFullName+":"+ projectName);

            
            Microsoft.VisualStudio.Text.Editor.IWpfTextView textView = GetTextView();
            Microsoft.VisualStudio.Text.SnapshotPoint caretPosition = textView.Caret.Position.BufferPosition;
            Microsoft.CodeAnalysis.Document document = caretPosition.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
            
            var methods=  document.GetSyntaxRootAsync().Result.DescendantNodes().OfType<MethodDeclarationSyntax>();
            var lastMethod = methods?.LastOrDefault();
            if (lastMethod != null)
            {
                frmInputFunction frmInputFunction = new frmInputFunction(textView, lastMethod.Span.End);
                frmInputFunction.ShowDialog();
            }
        }

        /// <summary>
        /// 获取默认浏览器的路径
        /// </summary>
        /// <returns></returns>
        public String GetDefaultWebBrowserFilePath()
        {
            string _BrowserKey1 = @"Software\Clients\StartmenuInternet\";
            string _BrowserKey2 = @"\shell\open\command";
 
            RegistryKey _RegistryKey = Registry.CurrentUser.OpenSubKey(_BrowserKey1, false);
            if (_RegistryKey == null)
                _RegistryKey = Registry.LocalMachine.OpenSubKey(_BrowserKey1, false);
            String _Result = _RegistryKey.GetValue("").ToString();
            _RegistryKey.Close();
 
            _RegistryKey = Registry.LocalMachine.OpenSubKey(_BrowserKey1 + _Result + _BrowserKey2);
            _Result = _RegistryKey.GetValue("").ToString();
            _RegistryKey.Close();
 
            if (_Result.Contains("\""))
            {
                _Result = _Result.TrimStart('"');
                _Result = _Result.Substring(0, _Result.IndexOf('"'));
            }
            return _Result;
        }
        /// <summary>
        /// 使用默认的浏览器打开指定的url地址
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GoToUrl(string url)
        {
            string BrowserPath = GetDefaultWebBrowserFilePath();
            System.Diagnostics.Process.Start(BrowserPath, url);
        }
        public const string aa = "32323";
        [NFinal.DebugAjax("dfsdf")]
        public void GetActionData(MethodDeclarationSyntax method, SemanticModel model)
        {
            var attributeList= method.AttributeLists;
            string str;
            foreach (var attributes in attributeList)
            {
                foreach (var attribute in attributes.Attributes)
                {
                    var typeInfo = model.GetTypeInfo(attribute);
                    string attributeType= typeInfo.Type.OriginalDefinition.ToDisplayString();
                    if (attributeType == "NFinal.Debug.DebugAjax")
                    {
                        foreach (var argument in attribute.ArgumentList.Arguments)
                        {
                            var argumentValue = model.GetConstantValue(argument.Expression);
                            str = (string)argumentValue.Value;
                        }
                    }
                    else if (attributeType == "NFinal.Debug.DebugPostData")
                    {
                        foreach (var argument in attribute.ArgumentList.Arguments)
                        {
                            var argumentValue = model.GetConstantValue(argument.Expression);
                            str = (string)argumentValue.Value;
                        }
                    }
                    else if (attributeType == "NFinal.DebugQueryString")
                    {
                        foreach (var argument in attribute.ArgumentList.Arguments)
                        {
                            var argumentValue = model.GetConstantValue(argument.Expression);
                            str = (string)argumentValue.Value;
                        }
                    }
                    else if (attributeType == "Route")
                    {
                        foreach (var argument in attribute.ArgumentList.Arguments)
                        {

                        }
                    }
                    else if (attributeType == "NFinal.HttpGet")
                    {

                    }
                    else if (attributeType == "NFinal.HttpPost")
                    {

                    }
                }
            }
        }
        private bool IsActionResultType(TypeSyntax type, SemanticModel model)
        {
            INamedTypeSymbol typeInfo;
            ITypeSymbol typeSymbol;
            ITypeSymbol typeSymbol2;
            string interfaceName = null;
            bool isInterface = false;
            string baseTypeName = null;
            System.Collections.Immutable.ImmutableArray<INamedTypeSymbol> interfaces = new System.Collections.Immutable.ImmutableArray<INamedTypeSymbol>();
            if (type is IdentifierNameSyntax)
            {
                var identifierNameSyntax = (IdentifierNameSyntax)type;
                typeSymbol = model.GetTypeInfo(identifierNameSyntax).Type;
                if (typeSymbol is INamedTypeSymbol)
                {
                    if (typeSymbol.TypeKind == TypeKind.Interface)
                    {
                        isInterface = true;
                        interfaceName = typeSymbol.OriginalDefinition.ToDisplayString();
                    }
                    else
                    {
                        typeInfo = (INamedTypeSymbol)typeSymbol;
                        interfaces = typeInfo.AllInterfaces;
                        if (typeInfo.TypeKind == TypeKind.Class)
                        {
                            var baseType = typeInfo;
                            while (baseTypeName != "object")
                            {
                                baseTypeName = baseType.OriginalDefinition.ToDisplayString();
                                baseType = typeInfo.BaseType;
                                if (baseTypeName == "System.Web.Mvc.ActionResult")
                                {
                                    break;
                                }
                                else if (baseTypeName == baseType.ToDisplayString())
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            else if (type is GenericNameSyntax)
            {
                var genericNameSyntax = (GenericNameSyntax)type;
                typeSymbol = model.GetTypeInfo(genericNameSyntax).Type;
                if (typeSymbol is INamedTypeSymbol)
                {
                    typeInfo = (INamedTypeSymbol)model.GetTypeInfo(genericNameSyntax).Type;
                    if (typeInfo.BaseType.ToDisplayString() == "System.Threading.Tasks.Task")
                    {
                        var arguments = genericNameSyntax.TypeArgumentList.Arguments;
                        if (arguments.Count == 1)
                        {
                            typeSymbol2 = model.GetTypeInfo(arguments[0]).Type;
                            if (typeSymbol2 is INamedTypeSymbol)
                            {
                                if (typeSymbol2.TypeKind == TypeKind.Interface)
                                {
                                    isInterface = true;
                                    interfaceName = typeSymbol2.OriginalDefinition.ToDisplayString();
                                }
                                else
                                {
                                    interfaces = model.GetTypeInfo((arguments[0])).Type.AllInterfaces;
                                    if (typeSymbol2.TypeKind == TypeKind.Class)
                                    {
                                        while (baseTypeName != "object")
                                        {
                                            var baseType = typeInfo;
                                            while (baseTypeName != "object")
                                            {
                                                baseTypeName = baseType.OriginalDefinition.ToDisplayString();
                                                baseType = typeInfo.BaseType;
                                                if (baseTypeName == "System.Web.Mvc.ActionResult")
                                                {
                                                    break;
                                                }
                                                else if (baseTypeName == baseType.ToDisplayString())
                                                {
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            bool hasIActionResult = false;
            if (isInterface)
            {
                if (interfaceName == "NFinal.IActionResult"
                    || interfaceName == "Microsoft.AspNetCore.Mvc.IActionResult"
                    || interfaceName == "System.Web.Mvc.ActionResult")
                {
                    hasIActionResult = true;
                }
            }
            else
            {
                if (interfaces != null)
                {
                    foreach (var inttemp in interfaces)
                    {
                        interfaceName = inttemp.OriginalDefinition.ToDisplayString();
                        if (interfaceName == "NFinal.IActionResult"
                            || interfaceName == "Microsoft.AspNetCore.Mvc.IActionResult"
                            || interfaceName == "System.Web.Mvc.ActionResult")
                        {
                            hasIActionResult = true;
                        }
                    }
                    if (baseTypeName == "System.Web.Mvc.ActionResult")
                    {
                        hasIActionResult = true;
                    }
                }
                else
                {
                    hasIActionResult = false;
                }
            }
            return hasIActionResult;
        }
        private void ShowMessage(string message)
        {
            VsShellUtilities.ShowMessageBox(
                this.ServiceProvider,
                message,
                "alert",
                OLEMSGICON.OLEMSGICON_INFO,
                OLEMSGBUTTON.OLEMSGBUTTON_OK,
                OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
        }
        private void MenuItemCreateViewCallback(object sender, EventArgs e)
        {

            ShowMessage("Create View");
        }
        private void MenuItemDebugToHereCallback(object sender, EventArgs e)
        {
            EnvDTE.TextSelection ts = dte.ActiveDocument.Selection as EnvDTE.TextSelection;
            EnvDTE80.CodeClass2 cls = ts.ActivePoint.CodeElement[vsCMElement.vsCMElementClass] as EnvDTE80.CodeClass2;
            string projectName = cls.ProjectItem.ContainingProject.Name;
            EnvDTE80.CodeFunction2 func = ts.ActivePoint.CodeElement[vsCMElement.vsCMElementFunction]
                as EnvDTE80.CodeFunction2;
            //ShowMessage(cls.Namespace.Name+":"+cls.Name+":"+func.Name+":"+func.Type.AsFullName+":"+ projectName);

            Microsoft.VisualStudio.Text.Editor.IWpfTextView textView = GetTextView();
            Microsoft.VisualStudio.Text.SnapshotPoint caretPosition = textView.Caret.Position.BufferPosition;
            Microsoft.CodeAnalysis.Document document = caretPosition.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

            //var workspace = componentModel.GetService<Microsoft.VisualStudio.LanguageServices.VisualStudioWorkspace>();
            //var project = workspace.CurrentSolution.Projects.Where(doc => { return doc.FilePath == projectName; }).FirstOrDefault();
            //Compilation compilation = null;
            //if (!project.TryGetCompilation(out compilation))
            //{
            //    compilation = project.GetCompilationAsync().Result;
            //}
            var rootNode = document.GetSyntaxRootAsync().Result;
            Microsoft.CodeAnalysis.SemanticModel semanticModel = document.GetSemanticModelAsync().Result;
            var methods = rootNode.DescendantNodes().OfType<MethodDeclarationSyntax>();
            int cusorPoint = ts.ActivePoint.AbsoluteCharOffset;
            MethodDeclarationSyntax currentMethod = null;
            foreach (var method in methods)
            {
                if (method.Span.Start < cusorPoint && method.Span.End > cusorPoint)
                {
                    currentMethod = method;
                    break;
                }
            }
            if (currentMethod != null)
            {
                if (dte.Mode == vsIDEMode.vsIDEModeDesign)
                {
                    bool isActionResult = IsActionResultType(currentMethod.ReturnType, semanticModel);
                    ShowMessage(isActionResult.ToString());
                    string debugFileName = Path.Combine(Path.GetDirectoryName(document.Project.FilePath),
                        "Debug",
                        cls.Namespace.Name.Replace('.', Path.DirectorySeparatorChar),
                        cls.Name.TrimSuffix("Controller"), func.Name + ".html");
                    dte.Debugger.Go(false);
                    if (dte.Mode == vsIDEMode.vsIDEModeDebug)
                    {
                        GoToUrl(debugFileName);
                    }
                }
            }
        }
        public TService GetService<TService>(Type tService)
        {
            MyCommandPackage myCommandPackage = (MyCommandPackage)this.package;
            return myCommandPackage.GetService<TService>(tService);
        }
        public TService GetService<TService>()
        {
            MyCommandPackage myCommandPackage = (MyCommandPackage)this.package;
            return myCommandPackage.GetService<TService>();
        }
        private void GetRightType()
        {
            //serviceProvider = package as System.IServiceProvider;
            //Microsoft.VisualStudio.Text.Editor.IWpfTextView textView = GetTextView();
            //Microsoft.VisualStudio.Text.SnapshotPoint caretPosition = textView.Caret.Position.BufferPosition;
            //Microsoft.CodeAnalysis.Document document = caretPosition.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
            //var rootNode= document.GetSyntaxRootAsync().Result;
            //Microsoft.CodeAnalysis.SemanticModel semanticModel = document.GetSemanticModelAsync().Result;
            //var methods= rootNode.DescendantNodes().OfType<MethodDeclarationSyntax>();
            //Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax invocationExpressionNode =
            //    document.GetSyntaxRootAsync().Result.FindToken(caretPosition).Parent.AncestorsAndSelf().
            //        OfType<Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax>().FirstOrDefault();
            //if (invocationExpressionNode != null)
            //{
                
            //    Microsoft.CodeAnalysis.IMethodSymbol methodSymbol =
            //        semanticModel.GetSymbolInfo(invocationExpressionNode).Symbol as
            //            Microsoft.CodeAnalysis.IMethodSymbol;
            //    textView.TextBuffer.Insert(invocationExpressionNode.SpanStart,
            //        methodSymbol.ReturnType.ToString() + " v = ");

            //}
        }
        private Microsoft.VisualStudio.Text.Editor.IWpfTextView GetTextView()
        {
            Microsoft.VisualStudio.TextManager.Interop.IVsTextManager textManager =
                this.GetService<Microsoft.VisualStudio.TextManager.Interop.IVsTextManager>(
                    typeof(Microsoft.VisualStudio.TextManager.Interop.SVsTextManager));
            Microsoft.VisualStudio.TextManager.Interop.IVsTextView textView;
            textManager.GetActiveView(1, null, out textView);
            return GetEditorAdaptersFactoryService().GetWpfTextView(textView);
        }

        private Microsoft.VisualStudio.Editor.IVsEditorAdaptersFactoryService GetEditorAdaptersFactoryService()
        {
            //Microsoft.VisualStudio.ComponentModelHost.IComponentModel componentModel =
            //    this.GetService<Microsoft.VisualStudio.ComponentModelHost.IComponentModel>(
            //        typeof(Microsoft.VisualStudio.ComponentModelHost.SComponentModel));
            return componentModel.GetService<Microsoft.VisualStudio.Editor.IVsEditorAdaptersFactoryService>();
        }
    }
}
